import _ from 'lodash';
import moment from 'moment';

import { JobContext } from './type';
import { StoragePath } from './simulationStoragePath';
import { StorageLogFlusher } from './service/storageFlusher';
import { LogLevel, isTrue, logger } from '$common';
import { agentConfig } from '$domain/model/agent/config';

type JobLoggerOptions = JobContext;
export class SimulationLogger {

  private debugMode = true;
  private ossEnable = true;
  private flusher: StorageLogFlusher;

  constructor(options: JobLoggerOptions) {
    if (options.debug !== undefined) {
      this.debugMode = options.debug;
    }
    const storagePath = StoragePath.of(_.omit(options, 'debug'));

    const flushOptions = {
      flushInterval: agentConfig.flushInterval,
      maxItems: agentConfig.flushMaxItems
    };

    const logPath = storagePath.getJobLogPath(options.jobId);
    this.flusher = new StorageLogFlusher(logPath, flushOptions);
  }

  private isDebugMode() {
    return isTrue(this.debugMode);
  }

  private log = (level: LogLevel, message: string, flush = false) => {

    switch (level) {
      case LogLevel.Debug:
        logger.debug(message);
        break;
      case LogLevel.Info:
        logger.info(message);
        break;
      case LogLevel.Warn:
        logger.warn(message);
        break;
      case LogLevel.Error:
        logger.error(message);
        break;
      default: {
        throw new Error(`unsupported log level: ${level}`);
      }
    }

    if (this.ossEnable) {
      this.flusher.add(`${moment().format('YYYY-MM-DD HH:mm:ss')} ==> ${message}`);
      if (flush) {
        this.flusher.flush();
      }
    }
  }

  disableOSS() {
    this.ossEnable = false;
  }

  enableOSS() {
    this.ossEnable = true;
  }
  
  info = (message: string, flush = false) => {
    this.log(LogLevel.Info, message, flush);
  }

  debug = (message: string, flush = false) => {
    this.log(LogLevel.Debug, message, flush);
  }

  warn = (message: string, flush = false) => {
    this.log(LogLevel.Warn, message, flush);
  }

  error = (message: string, flush = false) => {
    this.log(LogLevel.Error, message, flush);
  }
}